using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SbsSW.SwiPlCs;
using Adventure.Model;

namespace Adventure
{
    internal class Adventure : Game
    {
        #region Constants

        private const int WORLD_WIDTH = 320;
        private const int WORLD_HEIGHT = 193;
        private const int SCREEN_WIDTH = 1600;
        private const int SCREEN_HEIGHT = 900;
        private const bool FULLSCREEN = true;

        #endregion

        #region Constructors

        internal Adventure()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        #endregion

        #region Private Fields

        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private Rectangle _bounds;
        private HUD _hud;
        private Universe _universe;
        private KeyboardState _lastKeyboardState;

        #endregion

        #region Overriden Methods

        protected override void Initialize()
        {
            Environment.SetEnvironmentVariable("PATH", @"C:\swipl\bin");
            Environment.SetEnvironmentVariable("SWI_HOME_DIR", @"C:\swipl");
            PlEngine.Initialize(new[] { "-q", @"Prolog\Adventure.pl" });

            _graphics.PreferredBackBufferWidth = SCREEN_WIDTH;
            _graphics.PreferredBackBufferHeight = SCREEN_HEIGHT;
            _graphics.IsFullScreen = FULLSCREEN;
            _graphics.ApplyChanges();

            _bounds = new Rectangle(0, 0, WORLD_WIDTH, WORLD_HEIGHT);

            _universe = new Universe(_bounds);
            _hud = new HUD(_bounds, _universe);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(this.GraphicsDevice);

            GraphicsResources.SpriteBatch = _spriteBatch;
            GraphicsResources.ContentManager = this.Content;

            _hud.Initialize();
            _universe.Initialize();
        }

        protected override void UnloadContent()
        {
            PlEngine.PlCleanup();
        }

        protected override void Update(GameTime gameTime)
        {
            var keyboard = Keyboard.GetState();

            if (_lastKeyboardState != null && _lastKeyboardState.IsKeyDown(Keys.Escape) && !keyboard.IsKeyDown(Keys.Escape))
                this.Exit();

            if (_lastKeyboardState != null && _lastKeyboardState.IsKeyDown(Keys.F1) && !keyboard.IsKeyDown(Keys.F1))
            {
                this.UnloadContent();
                this.Initialize();
                this.LoadContent();
            }

            if (_lastKeyboardState != null && _lastKeyboardState.IsKeyDown(Keys.F2) && !keyboard.IsKeyDown(Keys.F2))
                PlQuery.PlCall("open('database.pl', write, S), set_output(S), listing, close(S)");

            _universe.Update(gameTime);

            _lastKeyboardState = keyboard;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            var target = new RenderTarget2D(this.GraphicsDevice, WORLD_WIDTH, WORLD_HEIGHT);
            GraphicsDevice.SetRenderTarget(target);

            this.GraphicsDevice.Clear(new Color(170, 170, 170));

            _hud.Update(gameTime);
            _universe.Draw(gameTime);
            _hud.Draw(gameTime);

            GraphicsDevice.SetRenderTarget(null);

            _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone);
            _spriteBatch.Draw(target, new Rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), Color.White);
            _spriteBatch.End();

            base.Draw(gameTime);
        }

        #endregion
    }
}
